/********************************************************************* 
 *					SRLOS Team Allright Reserved
 *@filename: thread.c
 *@autor:	bloceanc
 *@date:	08/04/2010
 *@note:	for process communication
 *********************************************************************/
 
#include "commdef.h"
#include "task.h"
#include "mem_config.h"

static process_share_page	*s_shared_list;	// shared page block list
static k_mutex	process_comm_mutex;	// mutex for operatin queue!

extern void mt_mutex_initset(k_mutex *mutex);
extern int mt_mutex_lock(k_mutex *mutex, long long time);
extern void mt_mutex_unlock(k_mutex *mutex);

extern void *kernel_allocate(unsigned int size);
extern void kernel_free(void *address);

extern void *__mmu_os_virtual_allocate(unsigned int page_count);
extern void __mmu_os_vritual_free(unsigned int page_viraddr, unsigned int page_count);

/*
 * Initialize this module
 */
void task_procomm_share_init(void)
{
	mt_mutex_initset(&process_comm_mutex);
	s_shared_list = NULL;
}

/*
 * 	insert new share into list.
 */
static void s_task_procomm_share_insert(process_share_page *share)
{
	share->next = NULL;
	share->prev = NULL;
	
	mt_mutex_lock(&process_comm_mutex, -1);
	if(s_shared_list == NULL)
		s_shared_list = share;
	else
	{
		share->next = s_shared_list;
		s_shared_list->prev = share;
		s_shared_list = share;
	}
	mt_mutex_unlock(&process_comm_mutex);
}

/*
 * 	delete share from list
 */
static void s_task_procomm_share_delete(process_share_page *share)
{
	mt_mutex_lock(&process_comm_mutex,-1);
	if(share->next == NULL && share->prev == NULL)
	{
		// the only one
		s_shared_list = NULL;
	}
	if(share->next == NULL && share->prev != NULL)
	{
		// the last one
		share->prev->next = NULL;
	}
	if(share->next != NULL && share->prev != NULL)
	{
		// normal
		share->prev->next = share->next;
		share->next->prev = share->prev;
	}
	if(share->next !=NULL && share->prev == NULL)
	{
		// the first one
		s_shared_list = share->next;
		s_shared_list->prev = NULL;
	}
	
	share->next = NULL;
	share->prev = NULL;
	mt_mutex_unlock(&process_comm_mutex);
}

/*
 *	create share memory of process.
 *@param	memory size wanted to shared.
 *@param	share handle value.user should specific a value to indicate a share memory block.
 * 			this specified by user!
 *@return	result of creating, success return 1
 * 			if handle value has existed or allocate memory failed, return 0!
 *@NOTE	: this function used for server process.
 */
int task_procomm_share_create(unsigned int size, unsigned int handle)
{
	process_share_page *tmp = NULL;
	void *page = NULL;
	
	if(size <= 0)
		return 0;
	
	// check whether handle has been existed
	tmp = s_shared_list;
	mt_mutex_lock(&process_comm_mutex, -1);
	while(tmp)
	{
		if(tmp->share_handle == handle)
		{
			mt_mutex_unlock(&process_comm_mutex);
			return 0;
		}
		tmp = tmp->next;
	}
	
	// reside size 4KB
	size = (size + MEM_CONFIG_PAGE_SIZE - 1)&~(MEM_CONFIG_PAGE_SIZE-1);
	// allocate block
	tmp = kernel_allocate(sizeof(process_share_page));
	if(tmp == NULL)
	{
		mt_mutex_unlock(&process_comm_mutex);
		return 0;
	}
	page = __mmu_os_virtual_allocate(size>>12);
	if(page == NULL)
	{
		kernel_free(tmp);
		mt_mutex_unlock(&process_comm_mutex);
		return 0;
	}
	tmp->next = NULL;
	tmp->prev = NULL;
	tmp->page_count = size>>12;
	tmp->process_count = 1;		// it must be 1 here.
	tmp->share_handle = handle;
	tmp->vir_start_addr = (unsigned int)page;
	s_task_procomm_share_insert(tmp);
	// Initialize access mutex
	mt_mutex_initset(&tmp->access_mutex);

	mt_mutex_unlock(&process_comm_mutex);
	return 1;
}

/*
 *	open shared memory
 *@param	handle	share handle
 *@return	success return 1, otherwise 0
 *@NOTE		:this used for clint process!
 */
int task_procomm_share_open(unsigned int handle)
{
	process_share_page *tmp = NULL;
	tmp = s_shared_list;
	while(tmp)
	{
		if(tmp->share_handle == handle)
		{
			tmp->process_count++;
			return 1;
		}
		tmp = tmp->next;
	}
	
	return 0;
}

/*
 *	close shared memory
 *@param	handle	shared handle
 *@return	void
 *@NOTE:	this function used for close/destroy shared memory by server process or clint process
 */
void task_procomm_share_close(unsigned int handle)
{
	process_share_page *tmp = NULL;
	tmp = s_shared_list;
	while(tmp)
	{
		if(tmp->share_handle == handle)
			break;
		tmp = tmp->next;
	}
	if(tmp == NULL)
	{
		// this is not share handle. so ,just return.
		return;
	}
	
	tmp->process_count--;	// sub reference
	if(tmp->process_count == 0)
	{
		// there is no process using this share. so delete it
		s_task_procomm_share_delete(tmp);
		// free memory page.
		__mmu_os_vritual_free(tmp->vir_start_addr, tmp->page_count);
		// free block memory
		kernel_free(tmp);
	}
}

/*
 *	write data to shared memory
 *@param	handle	shared handle
 *@param	buf		data buffer
 *@param	len		buffer length
 *@return	written size > 0, otherwise -1
 */
int	task_procomm_share_write(unsigned int handle, char *buf, int len)
{
	process_share_page *tmp = NULL;
	unsigned int *data_tmp = NULL;
	char *lea_dat_src = NULL;
	char *lea_dat_dst = NULL;
	unsigned int *dst = NULL;
	unsigned int i,j,count,lea;
	
	// looking for handle
	tmp = s_shared_list;
	while(tmp)
	{
		if(tmp->share_handle == handle)
		{
			break;
		}
		tmp = tmp->next;
	}
	
	if(tmp == NULL)
		return -1;
	
	if(len > (tmp->page_count << 12))
		return -2;
	
	// use access mutex lock
	mt_mutex_lock(&tmp->access_mutex,-1);

	data_tmp = (unsigned int *)buf;
	dst = (unsigned int *)tmp->vir_start_addr;
	count = len >> 2;
	for(i = 0; i < count; i++)
	{
		*(dst + i) = data_tmp[i];
	}
	
	lea_dat_dst = (char *)(dst+i);
	lea_dat_src = (char *)(data_tmp+i);
	
	lea = len & 0x3;
	for(j = 0; j < lea; j++)
	{
		lea_dat_dst[j] = lea_dat_src[j];
	}
	// unlock
	mt_mutex_unlock(&tmp->access_mutex);

	return len;
}

/*
 *	read data from share memory
 *@param	handle	shared handle
 *@param	buf		readed buffer
 *@param	len		readlen
 *@return	1 success, otherwise failed
 */
int task_procomm_share_read(unsigned int handle, char *buf, int len)
{
	unsigned int *dst = NULL;
	unsigned int *src = NULL;
	char *dst_lea = NULL;
	char *src_lea = NULL;
	int count = len >> 2;
	int lea_count = len & 0x3;
	int i,j;
	
	process_share_page *tmp = NULL;
	// find handle
	tmp = s_shared_list;
	while(tmp)
	{
		if(tmp->share_handle == handle)
			break;
		tmp = tmp->next;
	}
	
	if(tmp == NULL)
		return -1;
	
	if(len > (tmp->page_count<<12))
		return -2;
	
	// use access mutex lock operation.
	mt_mutex_lock(&tmp->access_mutex, -1);

	dst = (unsigned int *)buf;
	src = (unsigned int *)tmp->vir_start_addr;
	for(i = 0; i < count; i++)
	{
		*(dst + i) = *(src + i);
	}
	dst = dst + i;
	src = src + i;
	dst_lea = (char *)dst;
	src_lea = (char *)src;
	for(j = 0; j < lea_count; j++)
	{
		dst_lea[j] = src_lea[j];
	}
	// unlock
	mt_mutex_unlock(&tmp->access_mutex);

	return 1;
}

